<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

declare(encoding = 'UTF-8');
namespace crcl\base\mvc\view\html;

interface ITemplate
{
  /**
   * Returns Template instance from Command (factory)
   *
   * @static
   * @param \crcl\base\mvc\ICommand $oCommand
   * @return \crcl\base\mvc\view\html\ITemplate
   */
  public static function fromCommand(\crcl\base\mvc\ICommand $oCommand);

  /**
   * Returns compile template
   *
   * @return string
   */
  public function compile();
}

class Template extends \crcl\base\xml\sax\Builder implements ITemplate
{
  public static function fromCommand(\crcl\base\mvc\ICommand $oCommand)
  {
    $sContent = self::getTemplate($oCommand);
    $oTemplate = Template::fromString($sContent);
    $oTemplate->setValues($oCommand->getValues());

    return $oTemplate;
  }

  public function setValues(\crcl\base\mvc\IValues $oValues)
  {
    $this->oValues = $oValues;
  }

  public function compile()
  {
    $this->parse();
    $sContent = $this->getBuild();

    return $sContent;
  }

  //parser events (@see sax/Builder)

  public function onProcessingInstruction($sInstruction)
  {
    $sReturn = $sInstruction;
    $aInstruction = \explode(' ', $sInstruction);
    if (\strpos($sInstruction, '<?foundation') === 0)
    {
      $sReturn = '';
      foreach ($aInstruction as $key => $sCommand)
      {
        $aCommand = \explode(':', $sCommand);
        if (!isset($aCommand[2]))
        {
          $aCommand[2] = \NULL;
        }
        switch ($aCommand[0])
        {
          case 'fix':
            if ($aCommand[1] == 'html5shiv')
            {
              $sUrl = '<?php echo \crcl\base\system\Foundation::getUrl(\crcl\base\system\Foundation::URL_JS_HTML5SHIV); ?>';
              $sReturn = '<!--[if lt IE 9]><script src="' . $sUrl . '"></script><![endif]-->';
            }
            break;
          case 'css':
            if ($aCommand[1] == 'theme')
            {              
              $sUrl = '<?php echo \crcl\base\system\Foundation::getUrl(
                \crcl\base\system\Foundation::URL_CSS_THEME); ?>'; 
            }
            else
            {
              $sUrl = '<?php echo \crcl\base\system\Foundation::getUrl(\crcl\base\system\Foundation::URL_CSS);';
              $sUrl .= ' echo \'' . $aCommand[1] . '\'; ?>';
            }
            $sReturn = '<link rel="stylesheet" href="' . $sUrl . '" />';
            break;
          case 'js':
            switch ($aCommand[1])
            {
              case 'theme':
                $sUrl = '<?php echo \crcl\base\system\Foundation::getUrl(\crcl\base\system\Foundation::URL_JS_THEME); ?>';
                $sReturn = '<script src="' . $sUrl . '"></script>';
                break;
              case 'jquery':
                $sUrl = '<?php echo \crcl\base\system\Foundation::getUrl(\crcl\base\system\Foundation::URL_JS_JQUERY, \'' . $aCommand[2] . '\'); ?>';
                $sReturn = '<script src="' . $sUrl . '"></script>';
                break;
            }
            break;
          case 'img':
            if ($aCommand[1] == 'favicons')
            {
              $sUrlFavicon = '<?php echo \crcl\base\system\Foundation::getUrl(
                \crcl\base\system\Foundation::URL_IMG_FAVICON); ?>';
              $sUrlAppleIcon = '<?php echo \crcl\base\system\Foundation::getUrl(
                \crcl\base\system\Foundation::URL_IMG_APPLE_ICON); ?>';
              $sUrlAppleIcon72 = '<?php echo \crcl\base\system\Foundation::getUrl(
                \crcl\base\system\Foundation::URL_IMG_APPLE_ICON_72); ?>';
              $sUrlAppleIcon114 = '<?php echo \crcl\base\system\Foundation::getUrl(
                \crcl\base\system\Foundation::URL_IMG_APPLE_ICON_114); ?>';

              $sReturn =
                '<link rel="icon" type="image/x-icon" href="' . $sUrlFavicon . '" />' . "\n" .
                '<link rel="apple-touch-icon" href="' . $sUrlAppleIcon . '" />' . "\n" .
                '<link rel="apple-touch-icon" sizes="72x72" href="' . $sUrlAppleIcon72 . '" />' . "\n" .
                '<link rel="apple-touch-icon" sizes="114x114" href="' . $sUrlAppleIcon114 . '" />';
            }
            break;
        }

        if (\strpos($sCommand, 'remove:last') === 0)
        {
          $this->sBuild = \substr($this->sBuild, 0, $this->iLastTag);
        }
      }
    }
    if (\strpos($sInstruction, '<?base') === 0)
    {
      $sReturn = '';
      foreach ($aInstruction as $key => $sCommand)
      {
        if (\strpos($sCommand, 'include:') === 0)
        {
          //check defines
          foreach ($aInstruction as $sDefine)
          {
            if (\strpos($sDefine, 'define:') === 0)
            {
              $sDefine = \substr($sDefine, 7);
              $aDefine = \explode('=', $sDefine);              
              
              $oValue = \crcl\base\mvc\Value::getInstance('out:' . $aDefine[0] . ':string:"' . $aDefine[1]);    
              $this->oValues->putValue($aDefine[0], $oValue);
              break;
            }
          }
          $aInclude = \explode(':', $sCommand);
          $sFile = $aInclude[1];
          $sFile = \crcl\base\String::getInstance($sFile)
            ->doNotStartWith('"')
            ->doNotEndWith('"')
            ->toString();
          $sFile =
            \crcl\base\application\Path::getDirectory(\crcl\base\application\Path::DIR_TEMPLATES) . $sFile;
          $sReturn = @\file_get_contents($sFile);
          if ($sReturn === \FALSE)
          {
            throw new Exception(Exception::ERROR_TEMPLATE_NOFILE, array('file' => $sFile));
          }
          $oTemplate = Template::fromString($sReturn);
          $oTemplate->setValues($this->oValues);
          $sReturn = $oTemplate->compile();
        }
        if (\strpos($sCommand, 'value:') === 0)
        {
          $aValue = \explode(':', $sCommand);
          $sKey = $aValue[1];
          $sValue = $this->oValues->getValue($sKey)->get();
          $sReturn = \htmlspecialchars($sValue, \ENT_COMPAT, 'UTF-8'); //todo bei update auf 5.4 neue const
        }
        if (\strpos($sCommand, 'static:') === 0)
        {
          $aInclude = \explode(':', $sCommand);
          $sType = $aInclude[1];
          $sFile = $aInclude[2];

          $sFile = \crcl\base\String::getInstance($sFile)
            ->doNotStartWith('"')
            ->doNotEndWith('"')
            ->toString();

          if ($sType == 'css')
          {
            $sUrl =
              '<?php echo \crcl\base\application\Path::getUrl(\crcl\base\application\Path::URL_STATIC_CSS); echo \'' . $sFile . '\'; ?>';
            $sFile =
              \crcl\base\application\Path::getDirectory(
                \crcl\base\application\Path::DIR_STATIC_CSS) . $sFile;

            if (!\file_exists($sFile))
            {
              throw new Exception(Exception::ERROR_TEMPLATE_NOFILE, array('file' => $sFile));
            }
            $sReturn = '<link href="' . $sUrl . '" rel="stylesheet" />';
          }
          if ($sType == 'js')
          {
            $sUrl =
              '<?php echo \crcl\base\application\Path::getUrl(\crcl\base\application\Path::URL_STATIC_JS); echo \'' . $sFile . '\'; ?>';
            $sFile =
              \crcl\base\application\Path::getDirectory(\crcl\base\application\Path::DIR_STATIC_JS) .
              $sFile;

            if (!\file_exists($sFile))
            {
              throw new Exception(Exception::ERROR_TEMPLATE_NOFILE, array('file' => $sFile));
            }
            $sReturn = '<script src="' . $sUrl . '"></script>';
          }
        }
      }
    }

    parent::onProcessingInstruction($sReturn);
  }

  public function onElementStart($sTagName, $aAttributes, $bShort = \FALSE)
  {
    $this->iLastTag = \strlen($this->getBuild());

    foreach ($aAttributes as $sAttribute => $sValue)
    {
      if (\strpos($sValue, 'base:static:') === 0)
      {
          $aInclude = \explode(':', $sValue);
          $sType = $aInclude[2];
          $sFile = $aInclude[3];

          $sFile = \crcl\base\String::getInstance($sFile)
            ->doNotStartWith('"')
            ->doNotEndWith('"')
            ->toString();

          if ($sType == 'css')
          {
            $sUrl =
              '<?php echo \crcl\base\application\Path::getUrl(\crcl\base\application\Path::URL_STATIC_CSS); echo \'' . $sFile . '\'; ?>';
            $sFile =
              \crcl\base\application\Path::getDirectory(\crcl\base\application\Path::DIR_STATIC_CSS) .
              $sFile;

            if (!\file_exists($sFile))
            {
              throw new Exception(Exception::ERROR_TEMPLATE_NOFILE, array('file' => $sFile));
            }
            $aAttributes[$sAttribute] = $sUrl;            
          }
          if ($sType == 'js')
          {
            $sUrl =
              '<?php echo \crcl\base\application\Path::getUrl(\crcl\base\application\Path::URL_STATIC_JS); echo \'' . $sFile . '\'; ?>';
            $sFile =
              \crcl\base\application\Path::getDirectory(\crcl\base\application\Path::DIR_STATIC_JS) .
              $sFile;

            if (!\file_exists($sFile))
            {
              throw new Exception(Exception::ERROR_TEMPLATE_NOFILE, array('file' => $sFile));
            }
            $aAttributes[$sAttribute] = $sUrl;
          }
          if ($sType == 'img')
          {
            $sUrl =
              '<?php echo \crcl\base\application\Path::getUrl(\crcl\base\application\Path::URL_STATIC_IMG); echo \'' . $sFile . '\'; ?>';
            $sFile =
              \crcl\base\application\Path::getDirectory(\crcl\base\application\Path::DIR_STATIC_IMG) .
              $sFile;

            if (!\file_exists($sFile))
            {
              throw new Exception(Exception::ERROR_TEMPLATE_NOFILE, array('file' => $sFile));
            }
            $aAttributes[$sAttribute] = $sUrl;
          }          
        }
      if (\strpos($sValue, 'base:value:') === 0)
      {
        $sKey = \substr($sValue, 11); //strlen(base:value:)
        $sValue = $this->oValues->getValue($sKey)->get();
        $sValue = \htmlspecialchars($sValue, \ENT_COMPAT, 'UTF-8'); //todo bei update auf 5.4 neue const
        $aAttributes[$sAttribute] = $sValue;
      }
      if (\strpos($sValue, 'base:ifvalue:') === 0)
      {
        $sKey = \substr($sValue, 13); //strlen(base:value:)
        try
        {
          $sValue = $this->oValues->getValue($sKey)->get();
          $sValue = \htmlspecialchars($sValue, \ENT_COMPAT, 'UTF-8'); //todo bei update auf 5.4 neue const
          $aAttributes[$sAttribute] = $sValue;
        }
        catch (\Exception $e)
        {
          unset($aAttributes[$sAttribute]);
        }
      }
      if (\strpos($sValue, 'base:if:') === 0)
      {
        $sValues = '';
        $bTerm = \FALSE;
        $sTerm = \substr($sValue, 8); //strlen(base:if:)
        $iPos = \strpos($sTerm, '?'); 
        if ($iPos !== \FALSE)
        {
          $sValues = \substr($sTerm, $iPos+1);
          $sTerm = \substr($sTerm, 0, $iPos);
        }        
        $aValues = \explode(':', $sValues);
        if (!isset($aValues[0]))
        {
          $aValues[0] = \NULL;
        }
        if (!isset($aValues[1]))
        {
          $aValues[1] = \NULL;
        }        
        if (\strpos($sTerm, '=') !== \FALSE)
        {
          $aTerm = \explode('=', $sTerm);                    
          if ($aValues[0] == '' && $aValues[1] == '')
          {
            $aValues[0] = $this->oValues->getValue($aTerm[0])->get();
            $aValues[1] = \NULL;
          }          
          $aTerm[0] = $this->oValues->getValue($aTerm[0])->get();
          if ($aTerm[0] == $aTerm[1])
          {
            $bTerm = \TRUE;
          }
        }
        if ($bTerm)
        {
          $aAttributes[$sAttribute] = $aValues[0];
        }
        else
        {
          if (\is_null($aValues[1]))
          {
            unset($aAttributes[$sAttribute]);
          }
          else
          {
            $aAttributes[$sAttribute] = $aValues[1];
          }
        }        
      }
    }    
    parent::onElementStart($sTagName, $aAttributes, $bShort);
  }

  public function onXmlDeclaration($sDeclaration)
  {
    //do nothing ;)
  }

  /** PRIVATE *************************************************************************************/

  private $iLastTag = \NULL;
  private $oValues = \NULL;

  private static function getTemplate(\crcl\base\mvc\ICommand $oCommand)
  {
    $sFile = self::getTemplateFile($oCommand);
    $content = @\file_get_contents($sFile);
    if ($content === \FALSE)
    {
      throw new Exception(
        Exception::ERROR_TEMPLATE_NOTEMPLATE,
        array('file' => $sFile, 'command' => $oCommand));
    }
    return $content;
  }

  private static function getTemplateFile(\crcl\base\mvc\ICommand $oCommand)
  {
    $sDirectory = \crcl\base\application\Path::getDirectory(
      \crcl\base\application\Path::DIR_TEMPLATES);
//    if ($oCommand->hasVersion())
//    {
//      $sDirectory .= $oCommand->getVersion() . '/';
//    }
    if ($oCommand->hasGroup())
    {
      $sDirectory .= $oCommand->getGroup() . '/';
    }
    $sFile = $sDirectory . \strtolower($oCommand->getController()) . '/' .
      $oCommand->getAction() . '.xhtml';

    return $sFile;
  }
}